import static org.junit.Assert.*;
import org.junit.Test;

/**
 * This test suite would be a massacre if toString() didn't work correctly.
 * 
 * @author Sam Snyder
 *
 */
public class BigNaturalTest {
	
	/**
	 * It's probably really difficult to make a BigNatural that's susceptible to this, 
	 * but just in case some out there are.
	 */
	@Test public void defensiveCopy() {
		int i = 5;
		BigNatural b = createFromInt(i);
		i = 6;
		assertEquals("Failed to defensively copy when constructing with an int.", "5", b.toString());
		
		@SuppressWarnings("unused")
		String j = b.toString();
		j = "6";
		assertEquals("Failed to defensively copy when retrieving value with toString.", "5", b.toString());
	}
	
	/**
	 * Check that increment and decrement do not change the internal value of a BigNatural
	 */
	@Test public void immutability() {
		BigNatural b = createFromInt(15);
		b.increment();
		b.increment();
		assertEquals("Increment method behaved as a mutator.", "15", b.toString());
		b = createFromInt(15); // Just in case Increment is a mutator and decrement isn't
		b.decrement();
		b.decrement();
		assertEquals("Decrement method behaved as a mutator.", "15", b.toString());
	}
	
	/**
	 * Test all four constructors with a small value, i.e. one within the legal range
	 * for an int. 
	 */
	@Test public void smallNumConstructors() {
		BigNatural b = create();
		BigNatural b1 = createFromInt(100);
		BigNatural b2 = createFromString("100");
		BigNatural b3 = createFromBigNatural(b2);
		
		assertEquals("Construction with the default constructor failed.", "0", b.toString());
		assertEquals("Construction with a small int failed.", "100", b1.toString());
		assertEquals("Construction with a small String failed", "100", b2.toString());
		assertEquals("Construction with a small BigNatural failed", "100", b3.toString());
	}
	
	/**
	 * Test applicable constructors with a medium value, i.e. the max integer
	 */
	@Test public void mediumNumConstructors() {
		BigNatural b1 = createFromInt(Integer.MAX_VALUE);
		BigNatural b2 = createFromString(""+Integer.MAX_VALUE);
		BigNatural b3 = createFromBigNatural(b2);
		
		assertEquals("Construction with a medium int failed.", ""+Integer.MAX_VALUE, b1.toString());
		assertEquals("Construction with a medium String failed", ""+Integer.MAX_VALUE, b2.toString());
		assertEquals("Construction with a medium BigNatural failed", ""+Integer.MAX_VALUE, b3.toString());
	}
	
	/**
	 * Test constructors with numbers much larger than anything an int could represent. e.g.
	 * 21,474,836,472,147,483,647
	 */
	@Test public void largeNumConstructors() {
		BigNatural b1 = createFromString(Integer.MAX_VALUE+""+Integer.MAX_VALUE);
		BigNatural b2 = createFromBigNatural(b1);
		assertEquals("Construction with a large String failed", Integer.MAX_VALUE+""+Integer.MAX_VALUE, b1.toString());
		assertEquals("Construction with a large BigNatural failed", Integer.MAX_VALUE+""+Integer.MAX_VALUE, b2.toString());
	}
	
	/**
	 *  Test basic incrementing functionality.
	 */
	@Test public void basicIncrement() {
		BigNatural b = create();
		b = b.increment();
		b = b.increment();
		b = b.increment();
		assertEquals("Failed to increment from 0 to 3", "3", b.toString());
	}
	
	/**
	 * Test that digits are carried when '9' is incremented and that the carry bit stops
	 * at the appropriate place.
	 * 
	 */
	@Test public void carryIncrement() {
		BigNatural b = createFromInt(99099);
		b = b.increment();
		b = b.increment();
		assertEquals("Failed to increment from 99099 to 99101", "99101", b.toString());
	}
	
	/**
	 * Test that a '1' is successfully prepended when incremented to a new decimal place
	 */
	@Test public void allIncrement() {
		BigNatural b = createFromInt(99999);
		b = b.increment();
		assertEquals("Failed to increment from 99999 to 100000", "100000", b.toString());
	}
	/**
	 * Test basic decrementing functionality
	 */
	@Test public void basicDecrement() {
		BigNatural b = createFromInt(3);
		b = b.decrement();
		b = b.decrement();
		b = b.decrement();
		assertEquals("Failed to decrement from 3 to 0", "0", b.toString());
	}
	
	/**
	 * Test decrementing when digits will be carried.
	 */
	@Test public void carryDecrement() {
		BigNatural b = createFromInt(99101);
		b = b.decrement();
		b = b.decrement();
		assertEquals("Failed to decrement from 99101 to 99099", "99099", b.toString());
	}
	
	/**
	 * Test that the leading '0' is removed when decrementing removes a decimal place
	 */
	@Test public void allDecrement() {
		BigNatural b = createFromInt(100000);
		b = b.decrement();
		assertEquals("Failed to decrement from 100000 to 99999", "99999", b.toString());
	}
	
	/**
	 * Check that 0 decrements to 0.
	 */
	@Test public void zeroDecrement() {
		BigNatural b = create();
		b = b.decrement();
		assertEquals("Failed to decrement from 0 to 0", "0", b.toString());
	}
	
	/**
	 * Do it all with a REALLY BIG number, just in case something slipped through a crack
	 * in a previous test case.
	 */
	@Test public void bigHugeIncrementDecrement() {
		String hugenum = "99999999999999999999999999999999999999999999999999999999999999";
		hugenum = hugenum + hugenum;
		BigNatural b = createFromString(hugenum);
		b = b.increment();
		b = b.increment();
		b = b.decrement();
		b = b.decrement();
		assertEquals("Failed to increment/decrement with a REALLY BIG number.", hugenum, b.toString());
	}
	
	
	BigNatural create() { return new SlowBigNatural(); }
	BigNatural createFromInt(int val) { return new SlowBigNatural(val); }
	BigNatural createFromString(String val) { return new SlowBigNatural(val); }
	BigNatural createFromBigNatural(BigNatural someB) { return new SlowBigNatural(someB); }
}
